﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;

namespace DymaticMethod
{
    class AutoMapper
    {
        private static Dictionary<string, FieldInfo[]> _maps = new Dictionary<string, FieldInfo[]>();
        private static Dictionary<string, IList<DynamicMethod>> _mapDs = new Dictionary<string, IList<DynamicMethod>>();

        public static List<M> Map<T, M>(T reader) where T : IDataReader
        {
            List<M> listM = new List<M>();
            Type type = typeof(M);
            FieldInfo[] fields = GetFieldInfos(type);
            M model;
            while (reader.Read())
            {
                model = Activator.CreateInstance<M>();
                foreach (var item in fields)
                {
                    try
                    {
                        Object o = reader[item.Name];
                        if (o is DBNull) continue;
                        item.SetValue(model, o);
                    }
                    catch (ArgumentException e)
                    {

                    }
                    catch (IndexOutOfRangeException e1)
                    {

                    }
                }
                listM.Add(model);
            }
            return listM;
        }

        public static List<M> MapB<T, M>(T reader) where T : IDataReader
        {
            List<M> listM = new List<M>();
            Type type = typeof(M);
            FieldInfo[] fields = GetFieldInfos(type);
            M model;
            while (reader.Read())
            {
                model = Activator.CreateInstance<M>();
                foreach (var item in fields)
                {
                    try
                    {
                        Object o = reader[item.Name];
                        if (o is DBNull) continue;
                        //item.SetValue(model, o);
                        Type x = item.FieldType;
                        DynamicMethod dm = new DynamicMethod("Set" + item.Name, null, new Type[] { type, item.FieldType }, typeof(AutoMapper));
                        ILGenerator il = dm.GetILGenerator();

                        // Load the instance of the object (argument 0) onto the stack
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldarg_1);

                        // Load the value of the object's field (fi) onto the stack
                        il.Emit(OpCodes.Stfld, item);

                        // return the value on the top of the stack
                        il.Emit(OpCodes.Ret);
                        dm.Invoke(null, new object[] { model, o });
                    }
                    catch (ArgumentException e)
                    {

                    }
                    catch (IndexOutOfRangeException e1)
                    {

                    }
                }
                listM.Add(model);
            }
            return listM;
        }


        public static List<M> MapD<T, M>(T reader) where T : IDataReader
        {
            List<M> listM = new List<M>();
            Type type = typeof(M);
            IList<DynamicMethod> methods = GetDynamicMethod(type);
            M model;
            while (reader.Read())
            {
                model = Activator.CreateInstance<M>();
                foreach (var item in methods)
                {
                    try
                    {
                        Object o = reader[item.Name];
                        if (o is DBNull) continue;
                        item.Invoke(null, new object[] { model, o });
                    }
                    catch (ArgumentException e)
                    {

                    }
                    catch (IndexOutOfRangeException e1)
                    {

                    }
                }
                listM.Add(model);
            }
            return listM;
        }


        private static IList<DynamicMethod> GetDynamicMethods(Type mType)
        {
            IList<DynamicMethod> result = new List<DynamicMethod>();
            foreach (var item in mType.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                DynamicMethod dm = new DynamicMethod(item.Name, null, new Type[] { mType, item.FieldType }, typeof(AutoMapper));
                ILGenerator il = dm.GetILGenerator();
                // Load the instance of the object (argument 0) onto the stack
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_1);

                // Load the value of the object's field (fi) onto the stack
                il.Emit(OpCodes.Stfld, item);

                // return the value on the top of the stack
                il.Emit(OpCodes.Ret);
                result.Add(dm);
            }

            return result;

        }

        private static string GetClassName(Type mType)
        {
            var keyName = "Copy_";
            keyName += mType.FullName.Replace(".", "_");
            keyName += "IDataReader";
            return keyName;
        }
        private static FieldInfo[] GetFieldInfos(Type mType)
        {
            string name = GetClassName(mType);
            FieldInfo[] fields;
            if (!_maps.TryGetValue(name, out fields))
            {
                fields = mType.GetFields(BindingFlags.Public | BindingFlags.Instance);
                _maps.Add(name, fields);
            }
            return fields;
        }
        private static IList<DynamicMethod> GetDynamicMethod(Type mType)
        {
            string name = GetClassName(mType);
            IList<DynamicMethod> methods = new List<DynamicMethod>();
            if (!_mapDs.TryGetValue(name, out methods))
            {
                methods = GetDynamicMethods(mType);
                _mapDs.Add(name, methods);
            }
            return methods;
        }
    }
}
